package me.hgj.mvvm.ext

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.jcark.arch.net.state.NetWorkMonitorManager
import kotlinx.coroutines.*
import me.hgj.mvvm.network.AppException
import me.hgj.mvvm.network.BaseResponse
import me.hgj.mvvm.network.Error
import me.hgj.mvvm.network.ExceptionHandle
import me.hgj.mvvm.network.PageStatusCode
import me.hgj.mvvm.base.activity.BaseVmActivity
import me.hgj.mvvm.base.fragment.BaseVmFragment
import me.hgj.mvvm.base.viewmodel.BaseViewModel
import me.hgj.mvvm.ext.util.loge
import me.hgj.mvvm.state.ResultState
import me.hgj.mvvm.state.paresException
import me.hgj.mvvm.state.paresResult

/**
 * 作者　: hegaojian
 * 时间　: 2020/4/8
 * 描述　:BaseViewModel请求协程封装
 */

/**
 * 显示页面状态，这里有个技巧，成功回调在第一个，其后两个带默认值的回调可省
 * @param resultState 接口返回值
 * @param onLoading 加载中
 * @param onSuccess 成功回调
 * @param onError 失败回调
 *
 */
fun <T> BaseVmActivity<*>.parseState(
    resultState: ResultState<T>,
    onSuccess: (T?) -> Unit,
    onError: ((AppException) -> Unit)? = null,
    onLoading: (() -> Unit)? = null
) {
    when (resultState) {
        is ResultState.Loading -> {
            showLoading(resultState.loadingMessage)
            onLoading?.run { this }
        }
        is ResultState.Success -> {
            dismissLoading()
            onSuccess(resultState.data)
        }
        is ResultState.Error -> {
            dismissLoading()
            onError?.run { this(resultState.error) }
        }
    }
}

/**
 * 显示页面状态，这里有个技巧，成功回调在第一个，其后两个带默认值的回调可省
 * @param resultState 接口返回值
 * @param onLoading 加载中
 * @param onSuccess 成功回调
 * @param onError 失败回调
 *
 */
fun <T> BaseVmFragment<*>.parseState(
    resultState: ResultState<T>,
    onSuccess: (T?) -> Unit,
    onError: ((AppException) -> Unit)? = null,
    onLoading: ((message: String) -> Unit)? = null
) {
    when (resultState) {
        is ResultState.Loading -> {
            if (onLoading == null) {
                showUILoading(resultState.loadingMessage)
            } else {
                onLoading.invoke(resultState.loadingMessage)
            }
        }
        is ResultState.Success -> {
            dismissUILoading()
            onSuccess(resultState.data)
        }
        is ResultState.Error -> {
            dismissUILoading()
            onError?.run { this(resultState.error) }
        }
    }
}


/**
 * net request 不校验请求结果数据是否是成功
 * @param block 请求体方法
 * @param resultState 请求回调的ResultState数据
 * @param isShowLoadingDialog 是否显示加载框
 * @param loadingMessage 加载框提示内容
 * @param isObservePageStatus 是否启用观察页面状态
 */
fun <T> BaseViewModel.request(
    block: suspend () -> BaseResponse<T>,
    resultState: MutableLiveData<ResultState<T>>,
    isShowLoadingDialog: Boolean = false,
    loadingMessage: String = "加载中",
    isObservePageStatus: Boolean = false
): Job {
    return viewModelScope.launch {
        runCatching {
            if (isShowLoadingDialog) {
                resultState.value = ResultState.onAppLoading(loadingMessage)
            }
            loadingText.postValue(loadingMessage)
            setPageStatusCode(PageStatusCode.PAGE_STATUS_LOADING, isObservePageStatus, isShowLoadingDialog)
            //请求体
            block()
        }.onSuccess {
            setPageStatusCode(PageStatusCode.PAGE_STATUS_CONTENT, isObservePageStatus)
            resultState.paresResult(it)
        }.onFailure {
            it.message?.loge()
            //打印错误栈信息
            it.printStackTrace()
            if (it is CancellationException) {
                //协程被取消，会报错，在这里不做处理
            } else if (!NetWorkMonitorManager.isNetworkAvailable()) {
                resultState.value = ResultState.onAppError(AppException(Error.NETWORK_OFF, it))
                setPageStatusCode(PageStatusCode.PAGE_STATUS_NETWORK_OFF, isObservePageStatus)
            } else {
                setPageStatusCode(PageStatusCode.PAGE_STATUS_ERROR, isObservePageStatus)
                resultState.paresException(it)
            }
        }
    }
}

/**
 * net request 不校验请求结果数据是否是成功
 * @param block 请求体方法
 * @param resultState 请求回调的ResultState数据
 * @param isShowLoadingDialog 是否显示加载框
 * @param loadingMessage 加载框提示内容
 * @param isObservePageStatus 是否启用观察页面状态
 */
fun <T> BaseViewModel.requestNoCheck(
    block: suspend () -> T,
    resultState: MutableLiveData<ResultState<T>>,
    isShowLoadingDialog: Boolean = false,
    loadingMessage: String = "加载中",
    isObservePageStatus: Boolean = false
): Job {
    return viewModelScope.launch {
        runCatching {
            if (isShowLoadingDialog) resultState.value = ResultState.onAppLoading(loadingMessage)
            loadingText.postValue(loadingMessage)
            setPageStatusCode(PageStatusCode.PAGE_STATUS_LOADING, isObservePageStatus, isShowLoadingDialog)
            //请求体
            block()
        }.onSuccess {
            setPageStatusCode(PageStatusCode.PAGE_STATUS_CONTENT, isObservePageStatus)
            resultState.paresResult(it)
        }.onFailure {
            it.message?.loge()
            //打印错误栈信息
            it.printStackTrace()
            //resultState.paresException(it)

            if (it is CancellationException) {
                //协程被取消，会报错，在这里不做处理
            } else if (!NetWorkMonitorManager.isNetworkAvailable()) {
                setPageStatusCode(PageStatusCode.PAGE_STATUS_NETWORK_OFF, isObservePageStatus)
                resultState.value = ResultState.onAppError(AppException(Error.NETWORK_OFF, it))
            } else {
                setPageStatusCode(PageStatusCode.PAGE_STATUS_ERROR, isObservePageStatus)
                resultState.paresException(it)
            }
        }
    }
}

/**
 * 过滤服务器结果，失败抛异常
 * @param block 请求体方法，必须要用suspend关键字修饰
 * @param success 成功回调
 * @param error 失败回调 可不传
 * @param isShowLoadingDialog 是否显示加载框
 * @param loadingMessage 加载框提示内容
 * @param isObservePageStatus 是否启用观察页面状态
 */
fun <T> BaseViewModel.request(
    block: suspend () -> BaseResponse<T>,
    success: (T?) -> Unit,
    error: (AppException) -> Unit = {},
    isShowLoadingDialog: Boolean = false,
    loadingMessage: String = "加载中",
    isObservePageStatus: Boolean = false
): Job {
    //如果需要弹窗 通知Activity/fragment弹窗
    return viewModelScope.launch {
        runCatching {
            loadingText.postValue(loadingMessage)
            setPageStatusCode(PageStatusCode.PAGE_STATUS_LOADING, isObservePageStatus, isShowLoadingDialog)
            //请求体
            block()
        }.onSuccess {
            //网络请求成功 关闭弹窗
            setPageStatusCode(PageStatusCode.PAGE_STATUS_CONTENT, isObservePageStatus)
            runCatching {
                //校验请求结果码是否正确，不正确会抛出异常走下面的onFailure
                executeResponse(it) { t ->
                    success(t)
                }
            }.onFailure { e ->
                //打印错误消息
                e.message?.loge()
                //打印错误栈信息
                e.printStackTrace()
                //失败回调
                if (it is CancellationException) {
                    //协程被取消，会报错，在这里不做处理
                } else if (!NetWorkMonitorManager.isNetworkAvailable()) {
                    setPageStatusCode(PageStatusCode.PAGE_STATUS_NETWORK_OFF, isObservePageStatus)
                    error(AppException(Error.NETWORK_OFF, e))
                } else {
                    setPageStatusCode(PageStatusCode.PAGE_STATUS_ERROR, isObservePageStatus)
                    error(ExceptionHandle.handleException(e))
                }
            }
        }.onFailure {
            //网络请求异常 关闭弹窗
            //打印错误消息
            it.message?.loge()
            //打印错误栈信息
            it.printStackTrace()
            //失败回调
            if (it is CancellationException) {
                //协程被取消，会报错，在这里不做处理
            } else if (!NetWorkMonitorManager.isNetworkAvailable()) {
                setPageStatusCode(PageStatusCode.PAGE_STATUS_NETWORK_OFF, isObservePageStatus)
                error(AppException(Error.NETWORK_OFF, it))
            } else {
                setPageStatusCode(PageStatusCode.PAGE_STATUS_ERROR, isObservePageStatus)
                error(ExceptionHandle.handleException(it))
            }
        }
    }
}

/**
 *  不过滤请求结果
 * @param block 请求体 必须要用suspend关键字修饰
 * @param success 成功回调
 * @param error 失败回调 可不给
 * @param isShowLoadingDialog 是否显示加载框
 * @param loadingMessage 加载框提示内容
 * @param isObservePageStatus 是否启用观察页面状态
 */
fun <T> BaseViewModel.requestNoCheck(
    block: suspend () -> T,
    success: (T) -> Unit,
    error: (AppException) -> Unit = {},
    isShowLoadingDialog: Boolean = false,
    loadingMessage: String = "加载中",
    isObservePageStatus: Boolean = false
): Job {
    return viewModelScope.launch {
        runCatching {
            //如果需要弹窗 通知Activity/fragment弹窗
            loadingText.postValue(loadingMessage)
            setPageStatusCode(PageStatusCode.PAGE_STATUS_LOADING, isObservePageStatus, isShowLoadingDialog)
            //请求体
            block()
        }.onSuccess {
            setPageStatusCode(PageStatusCode.PAGE_STATUS_CONTENT, isObservePageStatus)
            //成功回调
            success(it)
        }.onFailure {
            //打印错误消息
            it.message?.loge()
            //打印错误栈信息
            it.printStackTrace()
            //失败回调
            if (it is CancellationException) {
                //协程被取消，会报错，在这里不做处理
            } else if (!NetWorkMonitorManager.isNetworkAvailable()) {
                setPageStatusCode(PageStatusCode.PAGE_STATUS_NETWORK_OFF, isObservePageStatus)
                error(AppException(Error.NETWORK_OFF, it))
            } else {
                setPageStatusCode(PageStatusCode.PAGE_STATUS_ERROR, isObservePageStatus)
                error(ExceptionHandle.handleException(it))
            }
        }
    }
}

/**
 * 请求结果过滤，判断请求服务器请求结果是否成功，不成功则会抛出异常
 */
suspend fun <T> executeResponse(
    response: BaseResponse<T>,
    success: suspend CoroutineScope.(T?) -> Unit
) {
    coroutineScope {
        when {
            response.isSuccess() -> {
                success(response.getResponseData())
            }
            else -> {
                throw AppException(
                    response.getResponseCode(),
                    response.getResponseMsg(),
                    response.getResponseMsg()
                )
            }
        }
    }
}

/**
 *  调用协程
 * @param block 操作耗时操作任务
 * @param success 成功回调
 * @param error 失败回调，可以不给出
 */
fun <T> BaseViewModel.launch(
    block: () -> T,
    success: (T) -> Unit,
    error: (Throwable) -> Unit = {}
): Job {
    return viewModelScope.launch {
        kotlin.runCatching {
            withContext(Dispatchers.IO) {
                block()
            }
        }.onSuccess {
            success(it)
        }.onFailure {
            error(it)
        }
    }
}

/**
 * 设置页面状态码值
 * @param pageStatusCodeValue 页面状态码值 @see [me.hgj.mvvm.network.PageStatusCode]
 * @param isSet 是否设置值
 */
private fun BaseViewModel.setPageStatusCode(pageStatusCodeValue: Int, isSet: Boolean, isShowLoading: Boolean = true) {
    setShowLoadingValue(pageStatusCodeValue == PageStatusCode.PAGE_STATUS_LOADING && isShowLoading)
    if (isSet) {
        pageStatusCode.postValue(pageStatusCodeValue)
    }
}

/**
 * 设置加载框是否显示标记值
 */
private fun BaseViewModel.setShowLoadingValue(isShow: Boolean) {
    showLoading.postValue(isShow)
}
